package mandelbrot.core;

import java.util.HashMap;

import com.ibm.tspaces.Field;
import com.ibm.tspaces.Tuple;
import com.ibm.tspaces.TupleSpace;
import com.ibm.tspaces.TupleSpaceException;

public class Worker implements Runnable {

	private int id;

	TupleSpace tupleSpace;

	// private double xstart;
	//
	// private double xend;
	//
	// private int width;
	//
	// private double ystart;
	//
	// private double yend;
	//
	// private int height;
	//
	// private int maxiter;

	private HashMap<Integer, MandelInfo> infos = new HashMap<Integer, MandelInfo>();

	public Worker(int id) {
		System.out.println("Starting Worker: " + id);
		this.id = id;
		try {
			tupleSpace = new TupleSpace("MandelBrotSpace", "localhost:8200");
		} catch (TupleSpaceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread workerThread = new Thread(this);
		workerThread.start();
	}

	public static void main(String[] args) {
		int tmp = (int) (Math.random() * 100);
		System.out.println("Worker started with ID: " + tmp);
		new Worker(tmp);
	}

	public void updateInfo(Integer maxiter, Integer width, Integer height, double xstart, double xend, double ystart, double yend, int id) {

		infos.put(id, new MandelInfo(maxiter, width, height, xstart, xend, ystart, yend));
		// this.maxiter = maxiter;
		// this.width = width;
		// this.height = height;
		// this.xstart = xstart;
		// this.xend = xend;
		// this.ystart = ystart;
		// this.yend = yend;
	}

	public void calculate(int xoff, int yoff, int xlen, int ylen, int id) {
		if(!infos.containsKey(id)){
			return;
		}
		MandelInfo mi = infos.get(id);
		int width = mi.width;
		int height = mi.height;
		double xstart = mi.xstart;
		double xend = mi.xend;
		double ystart = mi.ystart;
		double yend = mi.yend;
		int maxiter = mi.maxiter;
		int[] data = new int[width * height];
		int[][] d = new int[height][width];
		for (int y = yoff; y < yoff + ylen; y++) {
			for (int x = xoff; x < xoff + xlen; x++) {
				d[y][x] = iterate(xstart + (xend - xstart) * x / (width - 1), ystart + (yend - ystart) * y / (height - 1), maxiter) % 256;
			}
		}
		int i = 0;
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				data[i] = d[y][x];
				i++;
			}
		}
		try {
			Tuple response = new Tuple("RESP", data, xoff, yoff, xlen, ylen, this.id);
			response.add(id);
			tupleSpace.write(response);
		} catch (TupleSpaceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// store the results somewhere!
	}

	/**
	 * Calcluate the value for the given point.
	 * 
	 * @param x
	 *            x-value of the point
	 * @param y
	 *            y-value of the point.
	 * @return Value for the point.
	 */
	private int iterate(double x, double y, int maxiter) {
		int iter = 0;

		double aold = 0;
		double bold = 0;
		double a = 0;
		double b = 0;
		double asquared = a * a;
		double bsquared = b * b;

		a = x;
		b = y;

		double zsquared = asquared + bsquared;

		for (iter = 0; iter < maxiter; iter++) {
			a = asquared - bsquared + x;

			asquared = a * a;

			b = 2 * aold * b + y;

			if (bold == b && aold == a) {
				iter = maxiter - 1;
			}

			bsquared = b * b;

			zsquared = asquared + bsquared;

			if (zsquared > 4) {
				break;
			}

			bold = b;
			aold = a;
		}

		return iter;
	}

	@Override
	public void run() {
		for (;;) {

			try {

				

				Tuple t = new Tuple(new Field("REQ"), new Field(Integer.class), new Field(Integer.class), new Field(Integer.class), new Field(Integer.class), new Field(Integer.class));
				Tuple request = tupleSpace.waitToTake(t);
				
				Tuple template = new Tuple("INFO", new Field(Integer.class), new Field(Integer.class), new Field(Integer.class), new Field(Double.class),
						new Field(Double.class), new Field(Double.class));
				template.add(new Field(Double.class));
				template.add(new Field((Integer) request.getField(5).getValue()));

				Tuple info = tupleSpace.waitToRead(template);
				updateInfo((Integer) info.getField(1).getValue(), (Integer) info.getField(2).getValue(), (Integer) info.getField(3).getValue(), (Double) info
						.getField(4).getValue(), (Double) info.getField(5).getValue(), (Double) info.getField(6).getValue(), (Double) info.getField(7)
						.getValue(), (Integer) info.getField(8).getValue());
				
				calculate((Integer) request.getField(1).getValue(), (Integer) request.getField(2).getValue(), (Integer) request.getField(3).getValue(),
						(Integer) request.getField(4).getValue(), (Integer) request.getField(5).getValue());
			} catch (TupleSpaceException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}
}
